﻿/**
 * 
 * Copyright 2009, 2010 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using moose.irc;
using System.Windows.Forms;

namespace mooseirc.client.windowsforms {

    /// <summary>
    /// Our Status Form
    /// </summary>
    [CLSCompliant(true)]
    public sealed class StatusForm : ChildForm {

        #region [Local Attributes]

        /// <summary>
        /// Our IRC Session
        /// </summary>
        private IrcSession m_session;

        /// <summary>
        /// references input control
        /// </summary>
        private controls.InputControl input;

        /// <summary>
        /// references output control
        /// </summary>
        private controls.OututputControl output;

        /// <summary>
        /// Collection of communication channels
        /// </summary>
        private CommunicationChannelList m_communication_channels;

        #endregion

        #region [Construction]

        /// <summary>
        /// Initialize Status Form
        /// </summary>
        public StatusForm(MainForm parent_form)
            : base(parent_form,
                new controls.StatusControl()) {

            m_communication_channels = new CommunicationChannelList();

            m_session = new IrcSession();
            m_session.Event += new IrcEventHandler(m_session_Event);
            input = ((controls.StatusControl)m_HostedControl).input;
            output = ((controls.StatusControl)m_HostedControl).output;
            input.UserResponse += new ResponseHandler(input_UserResponse);

            wire_up_delegates(); //this needs to be called, or else IRC events wont have anything to fire...

            return;
        }

        #endregion

        #region [Event Handlers]

        /// <summary>
        /// Fires when user enters something
        /// </summary>
        /// <param name="response">The users response</param>
        void input_UserResponse(string response) {
            if (response.Length > 1) {
                if (response[0] == '/') {
                    m_session.send(response.Substring(1));
                }
            } else {
                if (m_session.SessionState == IrcSessionStates.None 
                    || m_session.SessionState == IrcSessionStates.Disconnected) {
                    output.write("***you are not connected to a server");
                } else {
                    output.write("***you are not in a channel");
                }
            }
            return;
        }

        /// <summary>
        /// Fires when an event happens from the IRC session
        /// </summary>
        /// <param name="session">session firing event</param>
        /// <param name="irc_event">the actual event</param>
        void m_session_Event(IrcSession session, IrcEvent irc_event) { //all of this has to fire from Control.Invoke, otherwise, we're gonna have cross threading issues
            IrcUser source = null;
            IrcChannel channel = null;
            string message = null;
            try {
                switch (irc_event.EventType) {
                    case IrcEventTypes.ServerConnected:
                        this.Invoke(register_delegate);
                        break;
                    case IrcEventTypes.ServerDisconnected:
                        this.Invoke(server_disconnected_delegate);
                        break;
                    case IrcEventTypes.ServerRegistered:
                        this.Invoke(server_registered_delegate);
                        break;
                    case IrcEventTypes.MessageStatus:
                        this.Invoke(write_status_delegate, (string)irc_event.Arguments[IrcEventArgumentType.Message].Value);
                        break;
                    case IrcEventTypes.NoticeMessage:
                        message = (string)irc_event.Arguments[IrcEventArgumentType.Message].Value;
                        source = (IrcUser)irc_event.Arguments[IrcEventArgumentType.SourceEntity].Value;
                        this.Invoke(write_status_delegate, " >> " + source.Nickname + " : " + message);
                        break;
                    case IrcEventTypes.PrivateMessage:
                        process_privmsg(irc_event);
                        break;
                    case IrcEventTypes.ChannelJoinedSelf:
                        channel = (IrcChannel)irc_event.Arguments[IrcEventArgumentType.DestinationEntity].Value;
                        this.Invoke(self_join_channel_delegate, channel);
                        break;
                    case IrcEventTypes.ChannelJoined:
                        source = (IrcUser)irc_event.Arguments[IrcEventArgumentType.SourceEntity].Value;
                        channel = (IrcChannel)irc_event.Arguments[IrcEventArgumentType.DestinationEntity].Value;
                        this.Invoke(join_channel_delegate, source, channel);
                        break;
                    case IrcEventTypes.ChannelPartedSelf:
                        channel = (IrcChannel)irc_event.Arguments[IrcEventArgumentType.DestinationEntity].Value;
                        this.Invoke(self_part_channel_delegate, channel);
                        break;
                    case IrcEventTypes.ChannelParted:
                        source = (IrcUser)irc_event.Arguments[IrcEventArgumentType.SourceEntity].Value;
                        channel = (IrcChannel)irc_event.Arguments[IrcEventArgumentType.DestinationEntity].Value;
                        this.Invoke(part_channel_delegate, source, channel);
                        break;
                    case IrcEventTypes.ChannelUpdateUsers:
                        channel = (IrcChannel)irc_event.Arguments[IrcEventArgumentType.DestinationEntity].Value;
                        this.Invoke(update_channel_users_delegate, channel);
                        break;
                    case IrcEventTypes.ChannelTopicChange:
                        channel = (IrcChannel)irc_event.Arguments[IrcEventArgumentType.DestinationEntity].Value;
                        this.Invoke(update_channel_topic_delegate, channel);
                        break;
                };

            } catch (Exception) {
                //This craps out when closing the status form, obviously because we cannot invoke 
                //off of a control that no longer exists.
                return;
            }
            return;
        }

        #endregion

        #region [Overrides]

        /// <summary>
        /// Fires when closing the form
        /// </summary>
        /// <param name="e">Cancel Event Args</param>
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e) {
            
            foreach (Form frm in m_communication_channels.get_all()) {
                frm.Close();
            }
            m_session.send("QUIT");

            base.OnClosing(e);
            return;
        }

        #endregion

        #region [Helper Methods]

        /// <summary>
        /// Process the Private Message Event
        /// </summary>
        /// <param name="privmsg_event"></param>
        /// <remarks>
        /// I broke this down into it's own method, so I could treat both source and destination as an entity..
        /// </remarks>
        private void process_privmsg(IrcEvent privmsg_event) {

            IrcEntity source = (IrcEntity)privmsg_event.Arguments[IrcEventArgumentType.SourceEntity].Value
                , dest = (IrcEntity)privmsg_event.Arguments[IrcEventArgumentType.DestinationEntity].Value;
            string message = (string)privmsg_event.Arguments[IrcEventArgumentType.Message].Value;

            this.Invoke(private_message_delegate, source, dest, message);

            return;
        }

        /// <summary>
        /// Attempt to register to the server
        /// </summary>
        private void register() {
            m_session.send("NICK " + moose.Settings.get(Properties.Resources.Setting_General_Identity_Nickname));
            m_session.send("USER " + moose.Settings.get(Properties.Resources.Setting_General_Identity_UserName) +
                " " +
                ((moose.Settings.get(Properties.Resources.Setting_General_Identity_Invisible) == "true") ? 8 : 0).ToString() +
                " * " +
                moose.Settings.get(Properties.Resources.Setting_General_Identity_Realname)
                );
            return;
        }

        #endregion

        #region [Delegates]

        /// <summary>
        /// Wires up the delegates, to functions, so all that magic happens...
        /// </summary>
        private void wire_up_delegates() {
            write_status_delegate += new ResponseHandler(write_status);
            self_join_channel_delegate += new GeneralChannelHandler(create_channel);
            join_channel_delegate += new ChannelHandler(user_join_channel);
            self_part_channel_delegate += new GeneralChannelHandler(part_channel);
            part_channel_delegate += new ChannelHandler(user_part_channel);
            update_channel_users_delegate += new GeneralChannelHandler(update_channel_users);
            private_message_delegate += new MessageHandler(private_message);
            register_delegate += new GenericHandler(register);
            server_disconnected_delegate += new GenericHandler(server_disconnected);
            server_registered_delegate += new GenericHandler(server_registered);
            update_channel_topic_delegate += new GeneralChannelHandler(update_channel_topic);
            return;
        }

        /// <summary>
        /// Used to call register function
        /// </summary>
        private GenericHandler register_delegate;

        /// <summary>
        /// used when disconnected from server
        /// </summary>
        private GenericHandler server_disconnected_delegate;

        /// <summary>
        /// used wehn registered to the server
        /// </summary>
        private GenericHandler server_registered_delegate;

        /// <summary>
        /// Handles requests to write to status
        /// </summary>
        private ResponseHandler write_status_delegate;

        /// <summary>
        /// Handles Private Messages
        /// </summary>
        private MessageHandler private_message_delegate;

        /// <summary>
        /// Handles when you join a channel
        /// </summary>
        private GeneralChannelHandler self_join_channel_delegate;

        /// <summary>
        /// handles when someone else joins a channel you're on
        /// </summary>
        private ChannelHandler join_channel_delegate;

        /// <summary>
        /// Handles when you part a channel
        /// </summary>
        private GeneralChannelHandler self_part_channel_delegate;

        /// <summary>
        /// handles when someone else parts a channel you're on
        /// </summary>
        private ChannelHandler part_channel_delegate;

        /// <summary>
        /// Handles requests to update channel user lists
        /// </summary>
        private GeneralChannelHandler update_channel_users_delegate;

        /// <summary>
        /// Handles requests to update channel topic
        /// </summary>
        private GeneralChannelHandler update_channel_topic_delegate;

        #endregion

        #region [Helper Functions]

        /// <summary>
        /// Write to status
        /// </summary>
        /// <param name="message">message to write</param>
        private void write_status(string message) {
            output.write(message);
            return;
        }

        /// <summary>
        /// Send Private Message to channel
        /// </summary>
        /// <param name="source">entity sending the message</param>
        /// <param name="dest">entity recieving the message</param>
        private void private_message(IrcEntity source, IrcEntity dest, string message) {
            CommunicationChannelForm communication_form = null;
            if (dest.GetType() == typeof(IrcChannel))
                communication_form = m_communication_channels.get(m_session, (MainForm)this.MdiParent, dest);
            else
                if (source.Name == (string)m_session.Settings_Server[IrcServerSettings.NICKNAME]) {
                    communication_form = m_communication_channels.get(m_session, (MainForm)this.MdiParent, dest);
                } else {
                    communication_form = m_communication_channels.get(m_session, (MainForm)this.MdiParent, source);
                }
            communication_form.write((IrcUser)source, message);
            if (!communication_form.Visible)
                communication_form.Show();
            return;
        }

        /// <summary>
        /// create a channel
        /// </summary>
        /// <param name="channel">channel to create</param>
        private void create_channel(IrcChannel channel) {
            ChannelForm channel_form = (ChannelForm)m_communication_channels.get(m_session, (MainForm)this.MdiParent, channel);
            channel_form.Show();
            return;
        }

        /// <summary>
        /// user joins channel
        /// </summary>
        /// <param name="user">user that joined</param>
        /// <param name="channel">channel that user joined</param>
        private void user_join_channel(IrcUser user, IrcChannel channel) {
            ChannelForm channel_form = (ChannelForm)m_communication_channels.get(m_session, (MainForm)this.MdiParent, channel);
            channel_form.write(user.Nickname + " joined the channel...");
            channel_form.update_users();
            return;
        }

        /// <summary>
        /// Part a channel
        /// </summary>
        /// <param name="channel"></param>
        private void part_channel(IrcChannel channel) {
            ChannelForm channel_form = (ChannelForm)m_communication_channels.get(m_session, (MainForm)this.MdiParent, channel);
            write_status("left " + channel.Name);
            channel_form.Close();
            m_communication_channels.remove(channel);
            return;
        }

        /// <summary>
        /// User parts a channel
        /// </summary>
        /// <param name="user">user that parts channel</param>
        /// <param name="channel">channel user parts</param>
        private void user_part_channel(IrcUser user, IrcChannel channel) {
            ChannelForm channel_form = (ChannelForm)m_communication_channels.get(m_session, (MainForm)this.MdiParent, channel);
            channel_form.write(user.Nickname + " left the channel...");
            channel_form.update_users();
            return;
        }

        /// <summary>
        /// Updates the user list in the channel
        /// </summary>
        /// <param name="channel">channel to update</param>
        private void update_channel_users(IrcChannel channel) {
            ChannelForm channel_form = (ChannelForm)m_communication_channels.get(m_session, (MainForm)this.MdiParent, channel);
            channel_form.update_users();
            return;
        }

        /// <summary>
        /// when registered, updates status, to show this
        /// </summary>
        private void server_registered() {
            Text = "Status " + m_session.Settings_Server[IrcServerSettings.NICKNAME].ToString();
            return;
        }

        /// <summary>
        /// cleans up all channel windows, and writes disconnected
        /// </summary>
        private void server_disconnected() {
            foreach (Form frm in m_communication_channels.get_all()) {
                frm.Close();
            }
            Text = "Status";
            write_status("***Disconnected");
            m_session.SessionState = IrcSessionStates.Disconnected;
            return;
        }

        /// <summary>
        /// Updates a channel topic
        /// </summary>
        /// <param name="channel">channel to update topic for</param>
        private void update_channel_topic(IrcChannel channel) {
            m_communication_channels.get(m_session, (MainForm)this.MdiParent, channel).Text = channel.Name + " - " + channel.Topic;
            return;
        }

        #endregion

    }
}
